/*
 * MODBUS Slave Library: A portable MODBUS slave for MODBUS ASCII/RTU/TCP.
 * Copyright (c) 2008 Christian Walter <cwalter@embedded-solutions.at>
 * Copyright (c) 2020 Embedded Experts GmbH <modbus@embedded-experts.at>
 *
 * All rights reserved. Use of this file is subject to license terms.
 */

#ifndef _MBS_H
#define _MBS_H

#include "mbport.h"
#include "mbtypes.h"
#include "mbframe.h"

/* ----------------------- Modbus includes ----------------------------------*/

/* ----------------------- Defines ------------------------------------------*/

/* ----------------------- Type definitions ---------------------------------*/
/*! \brief A handle to a MODBUS slave instance. */
typedef void *xMBSHandle;

/*! \brief If a register is written or read by the stack.*/
typedef enum
{
    /*! \brief The application values should be updated from the values passed
     * to the callback function.
     */
    MBS_REGISTER_WRITE,

    /*! \brief The application should store the current register values into
     * the buffer passed to the callback function.
     */
    MBS_REGISTER_READ
} eMBSRegisterMode;

/*! \brief Callback function if a <em>coil register</em> is read or
 *    written by the protocol stack.
 *
 * The callback may read or write up to 2000 coils where the first coil
 * is address by the parameter \c usAddress. If the coils are read by
 * the protocol stack the first coil should be written to the buffer
 * \c pubRegBuffer where the first 8 coils should be written to
 * pubRegBuffer[0], the second 8 coils to pubRegBuffer[1], ... If the
 * total amount is not a multiple of 8 the missing coils should be
 * set to zero.<br>
 * If the coils are written by the protocol stack, which is indicated
 * by the argument \c eRegMode set to eMBSRegisterMode::MBS_REGISTER_WRITE
 * , then the callback should update its coils with the values supplied
 * in \c pubRegBuffer. The enconding is the same as above.
 *
 * \param pubRegBuffer If the values are read by the stack the callback
 *   should update the buffer. Otherwise the callback can read the new
 *   status of the coils from this buffer.
 * \param usAddress Address of first coil.
 * \param usNRegs Number of coils.
 * \param eRegMode If set to eMBSRegisterMode::MBS_REGISTER_READ the
 *   coils are read by the protocol stack. In case of
 *   eMBSRegisterMode::MBS_REGISTER_WRITE the protocol stack needs to
 *   know the current value of the coil register.
 * \return If the callback returns eMBException::MB_PDU_EX_NONE a response is
 *   sent back to the master. Otherwise an appropriate exception frame is
 *   generated.
 */
typedef eMBException ( *peMBSCoilCB )( UBYTE *pubRegBuffer, USHORT usAddress, USHORT usNRegs, eMBSRegisterMode eRegMode );

/*! \brief Callback function if an <em>discrete register</em> is read by
 *    the protocol stack.
 *
 * The callback may request up to 2000 discrete inputs where the first
 * input to be returned is \c usAddress. The status of the discrete inputs
 * should be written to the buffer \c pubRegBuffer where the first
 * 8 registers should be written to pubRegBuffer[0], the second 8 registers
 * to pubRegBuffer[1]. If the total amount is not a multiple of 8 missing
 * values should be set to zero.
 *
 * \param pubRegBuffer Buffer where the status of the registers should
 *   be written to.
 * \param usAddress First discrete input to be returned.
 * \param usNRegs Number of registers requested.
 * \return If the callback returns eMBException::MB_PDU_EX_NONE a response is
 *   sent back to the master. Otherwise an appropriate exception frame is
 *   generated.
 */
typedef eMBException ( *peMBSDiscreteInputCB )( UBYTE *pubRegBuffer, USHORT usAddress, USHORT usNRegs ) ;

/*! \brief Callback function if an <em>input register</em> is read by the
 *   protocol stack.
 *
 * If the MODBUS slave stack needs to now the values of input registers this
 * callback function must store the current value of the registers starting
 * at\c usAddress to <tt>usAddress + usNRegs</tt> (not including the last one)
 * into the buffer \c pubRegBuffer. The 16 bit registers values must be stored
 * in big endian format.
 *
 * \param pubRegBuffer A pointer to an internal buffer. Exactly
 *   <tt>2 * usNRegs</tt> bytes must be written to this buffer.
 * \param usAddress The address of the first register which should be returned.
 *   Registers start at zero.
 * \param usNRegs The number of registers to read.
 *
 * \return If the callback returns eMBException::MB_PDU_EX_NONE a response is
 *   sent back to the master. Otherwise an appropriate exception frame is
 *   generated.
 */
typedef eMBException ( *peMBSRegisterInputCB )( UBYTE *pubRegBuffer, USHORT usAddress, USHORT usNRegs ) ;

/*! \brief Callback function if a <em>holding register</em> is read or
 *   written by the protocol stack.
 *
 * If the MODBUS slave stack needs to now the value of holding registers it
 * executes this callback with \c eRegisterMode = MBS_REGISTER_READ. The
 * callback function should then store the current values of the registers
 * starting at \c usAddress up to <tt>usAddress + usNRegs</tt> (not
 * including the last one) into the buffer \c pubRegBuffer. The 16 bit
 * registers values must be stored in big endian format.<br>
 * If the values should be updated the function is called with \c eRegisterMode
 * set to MBS_REGISTER_WRITE. The register values are passed in the buffer \c
 * pubRegBuffer and it is the applications responsibility to use these values.
 * The first register is stored in <tt>pubRegBuffer[0] - pubRegBuffer[1]</tt>
 * where the high byte comes first (big endian).
 *
 * \param pubRegBuffer A pointer to an internal buffer. If registers are read
 *   then exactly <tt>2 * usNRegs</tt> bytes must be written to this buffer.
 *   If registers are written the application can read up to
 *   <tt>2 * usNRegs</tt> from this buffer.
 * \param usAddress The address of the first register which should be returned.
 *   Registers start at zero.
 * \param usNRegs The number of registers to read.
 * \param eRegMode If the registers are read or written.
 *
 * \return If the callback returns eMBException::MB_PDU_EX_NONE a response is
 *   sent back to the master. Otherwise an appropriate exception frame is
 *   generated.
 */
typedef eMBException ( *peMBSRegisterHoldingCB )( UBYTE *pubRegBuffer, USHORT usAddress, USHORT usNRegs, eMBSRegisterMode eRegMode );


/* ----------------------- Function prototypes ------------------------------*/


/*! \brief Register a function callback for coils.
 *
 * \param xHdl A handle to a MODBUS slave instance.
 * \param peMBSCoilsCB A pointer to a function. This function is called
 *   whenever a coil is read or written. Use \c NULL to remove the
 *   previous callback.
 *
 * \return eMBErrorCode::MB_ENOERR if the input callback has been set or removed.
 *   eMBErrorCode::MB_EINVAL if the handle is not valid.
 */
eMBErrorCode eMBSRegisterCoilCB( xMBSHandle xHdl, peMBSCoilCB peMBSCoilsCB );

/*! \brief Register a function callback for discrete inputs.
 *
 * \param xHdl A handle to a MODBUS slave instance.
 * \param peDiscInputCB A pointer to a function. This function is called
 *   whenever a discrete register is read. Use \c NULL to remove the
 *   previous callback.
 *
 * \return eMBErrorCode::MB_ENOERR if the input callback has been set or removed.
 *   eMBErrorCode::MB_EINVAL if the handle is not valid.
 */
eMBErrorCode eMBSRegisterDiscreteCB( xMBSHandle xHdl, peMBSDiscreteInputCB peDiscInputCB );

/*! \brief Register a function callback for input registers.
 *
 * \param xHdl A handle to a MODBUS slave instance.
 * \param peRegInputCB A pointer to a function. This function is called
 *   whenever the value of an input register is required. Use \c NULL to remove
 *   a previous callback.
 *
 * \return eMBErrorCode::MB_ENOERR if the input callback has been set or removed.
 *   eMBErrorCode::MB_EINVAL if the handle is not valid.
 */
eMBErrorCode eMBSRegisterInputCB( xMBSHandle xHdl, peMBSRegisterInputCB peRegInputCB );

/*! \brief Register a function callback for holding registers.
 *
 * \param xHdl A handle to a MODBUS slave instance.
 * \param peRegHoldingCB A pointer to a function. This function is called
 *   whenever the value of a holding register is written or read. Use \c NULL to remove
 *   a previous callback.
 *
 * \return eMBErrorCode::MB_ENOERR if the input callback has been set or removed.
 *   eMBErrorCode::MB_EINVAL if the handle is not valid.
 */
eMBErrorCode eMBSRegisterHoldingCB( xMBSHandle xHdl, peMBSRegisterHoldingCB peRegHoldingCB );

/*! \brief Close the stack.
 *
 * Shutdown the slave stack. This function waits until all pending MODBUS
 * requests have been answered and then operation is stop. All resources
 * are returned to the porting layer.
 *
 * \param xHdl A handle for a MODBUS slave instances.
 * \return eMBErrorCode::MB_ENOERR if the stack has been shut down.
 */
eMBErrorCode eMBSClose( xMBSHandle xHdl );

/*! \brief The main polling loop of the MODBUS stack.
 *
 * This function must be called periodically. The timer interval required
 * is given by the application but a good starting point is somewhere around
 * 50ms. Internally the function checks if any events have happened and if
 * yes processes them.
 *
 * \note Most customers do not need to implement the handling of
 *   eMBErrorCode::MB_EILLSTATE. But for example if a PPP connection which
 *   might break is used a possible fallback would shut down the stack,
 *   reopen the connection and then start the stack again.
 *
 * \param xHdl The handle to poll.
 *
 * \return If the handle is not valid the function returns
 *   eMBErrorCode::MB_EINVAL. If an internal error occurred in the porting
 *   layer the function returns eMBErrorCode::MB_EILLSTATE and this
 *   protocol instance should be closed. Otherwise the function returns
 *   eMBErrorCode::MB_ENOERR.
 */
eMBErrorCode eMBSPoll( xMBSHandle xHdl );

/* ----------------------- Function prototypes ( Serial ) -------------------*/

/*!
 * \brief Create a new instances for a serial MODBUS slave instance using
 *   either ASCII or RTU transmission mode.
 *
 * Note that after the stack has been created function pointers for input,
 * holding, coil and discrete registers have to be provided. They can be set
 * by calling any of the functions:
 *
 *  - eMBSRegisterHoldingCB
 *  - eMBSRegisterInputCB
 *  - eMBSRegisterDiscreteCB
 *  - eMBSRegisterCoilCB
 *  - eMBSRegisterFileCB
 *
 * \note
 * In RTU mode 11 bits are used for each data byte. The coding system is
 * 8bit binary.
 *  - 1 start bit.
 *  - 8 data bits with LSB sent first.
 *  - 1 bit for parity (Even, Odd)
 *  - 1 or 2 stop bits (Two stopbits if no parity is used).
 *
 * In ASCII mode 10 bits are used. The coding system uses the hexadecimal
 * ASCII characters 0-8 and A-F. One hexadecimal characters contains 4-bits
 * of data.
 *  - 1 start bit
 *  - 7 data bits with LSB sent first.
 *  - 1 bit for parity (Even, Odd)
 *  - 1 or 2 stop bits (Two stopbits if no parity is used).
 *
 * \param pxHdl A pointer to a MODBUS handle. If the function returns
 *   eMBErrorCode::MB_ENOERR the handle is updated to hold a new and valid
 *   slave handle. This handle should never be modified by the user.
 * \param eMode The serial transmission mode to use. Either MB_RTU or MB_ASCII.
 * \param ubSlaveAddress The slave address. Only frames sent to this address
 *   or to the broadcast address are handled. Valid slave addresses are in
 *   the range 1 - 247.
 * \param ubPort The serial port to use. The meaning of this value depends on
 *   the porting layer.
 * \param ulBaudRate The baudrate. For example 38400.
 * \param eParity The parity to use.
 *
 * \return eMBErrorCode::MB_ENOERR if a new SLAVE has been created. Otherwise
 *   one of the following error codes is returned.
 *    - eMBErrorCode::MB_EINVAL If any of the arguments are not valid.
 *    - eMBErrorCode::MB_EPORTERR If the porting layer returned an error.
 */
eMBErrorCode eMBSSerialInit( xMBSHandle *pxHdl, eMBSerialMode eMode, UBYTE ubSlaveAddress, pusSendDatas pMBSFrameSendFN, pusReciveDatas pMBSFrameRecvFN);

#endif
